﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Noris.Tools.TraceVisualiser.Interface;
using Noris.Tools.TraceVisualiser.Support;
using Noris.Tools.TraceVisualiser.SqlParser;
using Noris.Tools.TraceVisualiser.Forms;
using Noris.Tools.TraceVisualiser.TraceData;
using Noris.Tools.TraceVisualiser.Green.AplServer;

namespace Noris.Tools.TraceVisualiser.Green
{
    /// <summary>
    /// Třída pro podporu funkcí, které pracují se SQL událostmi
    /// </summary>
    public class Func_SqlBase : Func_Base
    {
        #region Získání SQL eventů
        /// <summary>
        /// Vrátí SQL událost odpovídající elementu z vizuální vrstvy
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        protected EventSqlItem GetSelectedSqlEvent(FunctionContextMenuItemRunArgs args)
        {
            TraceData.TraceDataSource ds = args.DataSource as TraceData.TraceDataSource;
            if (ds == null) return null;
            TraceData.MainInfo main = ds.Main;

            DataPointerStr pointer = args.ClickedItem;
            if (pointer.IsEmpty && args.SelectedElements.Count > 0)
                pointer = args.SelectedElements[0];
            if (pointer.IsEmpty)
                return null;

            IEventItem eventItem = main.FindEvent(args.ClickedItem);
            if (eventItem == null) return null;
            if (!(eventItem is EventSqlItem)) return null;
            return eventItem as EventSqlItem;
        }
        /// <summary>
        /// Vrátí soupis SQL událostí podle výběru z vizuální vrstvy
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        protected List<EventSqlItem> GetSelectedSqlEvents(FunctionContextMenuItemRunArgs args)
        {
            return GetSelectedSqlEvents(args, null);
        }
        /// <summary>
        /// Vrátí soupis SQL událostí podle výběru z vizuální vrstvy
        /// </summary>
        /// <param name="args"></param>
        /// <returns></returns>
        protected List<EventSqlItem> GetSelectedSqlEvents(FunctionContextMenuItemRunArgs args, Int32? maxCount)
        {
            List<EventSqlItem> result = new List<EventSqlItem>();

            TraceVisualiser.TraceData.TraceDataSource ds = args.DataSource as TraceVisualiser.TraceData.TraceDataSource;
            if (ds == null) return result;

            List<KeyValuePair<DataPointerStr, IEventItem>> events = ds.Main.FindEvents(args.InputElements, new Func<IEventItem,bool>(iei => iei is EventSqlItem), maxCount);
            if (events == null || events.Count == 0) return result;


            foreach (KeyValuePair<DataPointerStr, IEventItem> pe in events)
            {
                if (pe.Value is EventSqlItem)
                    result.Add(pe.Value as EventSqlItem);
            }

            return result;
        }
        #endregion
        #region Podpora pro roztřídění SQL eventů do PID a transakcí
        /// <summary>
        /// Z hlavního datového balíku (main) načte všechny procesy, a z nich vyfiltruje všechny SQL a TRAN eventy (pro daný čas), a uloží je do výstupního seznamu.
        /// </summary>
        /// <param name="main"></param>
        /// <param name="sqlTime"></param>
        /// <returns></returns>
        protected Dictionary<int, SqlPidHeap> GetSqlPidHeapsForTime(TraceData.MainInfo main, TimeRange sqlTime, bool placeIntoTrans)
        {
            Dictionary<int, SqlPidHeap> pidHeaps = new Dictionary<int, SqlPidHeap>();
            foreach (ProcessInfo process in main.Processes)
            {	// Procesy v Main jsou souhrnem procesů ze všech serverů (proces == Session):
                foreach (EventInfo eventInfo in process.Events)
                {	// Každý proces obsahuje eventy = události v session, všechny typy (SQL, Cowley, Instance, jádro, atd):
                    if (eventInfo.MergedOut) continue;             // Tato událost byla vložena do jiné události, a je neviditelná
                    IEventItem eventItem = eventInfo.EventItem;
                    if ((eventItem is EventSqlItem || eventItem is EventTransItem) && !((sqlTime * eventInfo.Time).IsEmpty))
                    {	// Tuto SQL událost přidáme:
                        int pid = (eventItem is EventSqlItem ? (eventItem as EventSqlItem).SqlPId :
                                  (eventItem is EventTransItem ? (eventItem as EventTransItem).SqlPId : 0));

                        if (pid != 0)
                        {
                            SqlPidHeap pidHeap;
                            if (!pidHeaps.TryGetValue(pid, out pidHeap))
                            {
                                pidHeap = new SqlPidHeap(pid);
                                pidHeaps.Add(pid, pidHeap);
                            }
                            pidHeap.AddItem(eventItem);
                        }
                    }
                }
            }

            // Zajistím, že SQL commandy se vepíšou do časově příslušných transakcí. SQL commandy mimo transakce se zahodí:
            if (placeIntoTrans)
            {
                foreach (SqlPidHeap pidHeap in pidHeaps.Values)
                    pidHeap.PlaceSqlIntoTrans();
            }
            return pidHeaps;
        }
        /// <summary>
        /// Třída, která v sobě zahrnuje jednu SQL connection, a všechny její transakce, a v každé z nich pak všechny jejich SQL commandy
        /// </summary>
        protected class SqlPidHeap
        {
            internal SqlPidHeap(int sqlPid)
            {
                this.SqlPid = sqlPid;
                this.TransBlock = new List<SqlTransHeap>();
                this.SqlItemList = new List<EventSqlItem>();
            }
            public override string ToString()
            {
                return "PID=" + this.SqlPid.ToString() + "; Trans.Count=" + this.TransBlock.Count.ToString() + "; FreeSqlItem.Count=" + this.SqlItemList.Count.ToString();
            }
            internal int SqlPid { get; set; }
            internal List<SqlTransHeap> TransBlock { get; private set; }
            internal List<EventSqlItem> SqlItemList { get; private set; }
            /// <summary>
            /// Přidat item do patřičného soupisu
            /// </summary>
            /// <param name="eventItem"></param>
            internal void AddItem(IEventItem eventItem)
            {
                if (eventItem is EventSqlItem)
                    this.SqlItemList.Add(eventItem as EventSqlItem);
                else if (eventItem is EventTransItem)
                    this.TransBlock.Add(new SqlTransHeap(eventItem as EventTransItem));
            }
            /// <summary>
            /// Umístit SQL commandy do transakcí, neumístitelné eventy zahodit
            /// </summary>
            internal void PlaceSqlIntoTrans()
            {
                if (this.TransBlock.Count > 0)
                {
                    foreach (EventSqlItem sqlItem in this.SqlItemList)
                    {
                        SqlTransHeap trans = this.TransBlock.FirstOrDefault(t => t.TransItem.Time.Contain(sqlItem.Time));
                        if (trans != null)
                            trans.AddSqlItem(sqlItem);
                    }
                }
                this.SqlItemList.Clear();
            }
        }
        /// <summary>
        /// Třída, která v sobě zahrnuje jednu transakci a všechny SQL commandy, které proběhly v jejím rámci
        /// </summary>
        protected class SqlTransHeap
        {
            internal SqlTransHeap(EventTransItem transItem)
            {
                this.TransItem = transItem;
                this.SqlItemList = new List<EventSqlItem>();
            }
            public override string ToString()
            {
                return "Trans.Time=" + this.Time.StringBeginAndSecs + "; SqlItem.Count=" + this.SqlItemList.Count.ToString();
            }
            /// <summary>
            /// Čas této transakce
            /// </summary>
            internal TimeRange Time { get { return this.TransItem.Time; } }
            /// <summary>
            /// Transakce
            /// </summary>
            internal EventTransItem TransItem { get; private set; }
            /// <summary>
            /// SQL příkazy, které se odehrály v rámci (v čase a SqlPID) této transakce
            /// </summary>
            internal List<EventSqlItem> SqlItemList { get; private set; }
            /// <summary>
            /// Přidá do svého seznamu SQL eventů daný event
            /// </summary>
            /// <param name="sqlItem"></param>
            internal void AddSqlItem(EventSqlItem sqlItem)
            {
                this.SqlItemList.Add(sqlItem);
            }
        }
        #endregion
    }
}
